﻿using System;
using System.Linq;
using System.ComponentModel;
using Hangfire;
using Hangfire.AspNetCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using EFCore.Core.Extensions;

namespace EFCore.Hangfire
{
    /// <summary>
    /// Hangfire 后台任务模块
    /// </summary>
    [Description("Hangfire后台任务模块")]
    public class HangfirePack
    {
        /// <summary>
        /// 将模块服务添加到依赖注入服务容器中
        /// </summary>
        /// <param name="services">依赖注入服务容器</param>
        /// <returns></returns>
        public static IServiceCollection AddServices(IServiceCollection services)
        {
            IConfiguration configuration = (IConfiguration)services.FirstOrDefault(d => d.ServiceType == typeof(IConfiguration))?.ImplementationInstance;
            bool enabled = configuration["Hangfire:Enabled"].CastTo(false);
            if (!enabled)
            {
                return services;
            }

            Action<IGlobalConfiguration> hangfireAction = GetHangfireAction(services);
            services.AddHangfire(hangfireAction);
            //services.AddHangfireServer();
            return services;
        }

        /// <summary>
        /// 应用AspNetCore的服务业务
        /// </summary>
        /// <param name="app">Asp应用程序构建器</param>
        public static void UsePack(IApplicationBuilder app)
        {
            IServiceProvider serviceProvider = app.ApplicationServices;
            IConfiguration configuration = serviceProvider.GetService<IConfiguration>();
            bool enabled = configuration["Hangfire:Enabled"].CastTo(false);
            if (!enabled)
            {
                return;
            }

            IGlobalConfiguration globalConfiguration = serviceProvider.GetService<IGlobalConfiguration>();
            globalConfiguration.UseLogProvider(new AspNetCoreLogProvider(serviceProvider.GetService<ILoggerFactory>()));

            BackgroundJobServerOptions serverOptions = GetBackgroundJobServerOptions(configuration);
            app.UseHangfireServer(serverOptions);

            string url = configuration["Hangfire:DashboardUrl"].CastTo("/hangfire");
            DashboardOptions dashboardOptions = GetDashboardOptions(configuration);
            app.UseHangfireDashboard(url, dashboardOptions);

            IHangfireJobRunner jobRunner = serviceProvider.GetService<IHangfireJobRunner>();
            jobRunner?.Start();
        }

        /// <summary>
        /// AddHangfire委托，重写可配置Hangfire服务，比如使用UseSqlServerStorage等
        /// </summary>
        /// <param name="services">服务容器</param>
        /// <returns></returns>
        protected static Action<IGlobalConfiguration> GetHangfireAction(IServiceCollection services)
        {
            IConfiguration configuration = (IConfiguration)services.FirstOrDefault(d => d.ServiceType == typeof(IConfiguration))?.ImplementationInstance;
            string storageConnectionString = configuration["Hangfire:StorageConnectionString"].CastTo<string>();
            if (!string.IsNullOrWhiteSpace(storageConnectionString))
            {
                return config => config.UseSqlServerStorage(storageConnectionString);
            }

            return config => { };
        }

        /// <summary>
        /// 获取后台作业服务器选项
        /// </summary>
        /// <param name="configuration">系统配置信息</param>
        /// <returns></returns>
        protected static BackgroundJobServerOptions GetBackgroundJobServerOptions(IConfiguration configuration)
        {
            BackgroundJobServerOptions serverOptions = new BackgroundJobServerOptions();
            int workerCount = configuration["Hangfire:WorkerCount"].CastTo(0);
            if (workerCount > 0)
            {
                serverOptions.WorkerCount = workerCount;
            }
            return serverOptions;
        }

        /// <summary>
        /// 获取Hangfire仪表盘选项
        /// </summary>
        /// <param name="configuration">系统配置信息</param>
        /// <returns></returns>
        protected static DashboardOptions GetDashboardOptions(IConfiguration configuration)
        {
            string[] roles = configuration["Hangfire:Roles"].CastTo("").Split(",", true);
            string[] userIds = configuration["Hangfire:UserIds"].CastTo("").Split(",", true);
            string accessToken = configuration["Hangfire:AccessToken"].CastTo("");
            DashboardOptions dashboardOptions = new DashboardOptions();
            //限制角色存在时，才启用角色限制
            if (roles.Length > 0 || userIds.Length > 0 || !string.IsNullOrWhiteSpace(accessToken))
            {
                dashboardOptions.Authorization = new[] { new RoleDashboardAuthorizationFilter(roles, userIds, accessToken) };
            }
            return dashboardOptions;
        }
    }
}